part of 'win.dart';

/// 与Windows通讯
class WinMethodChannelBlue {
  static WinMethodChannelBlue _instance = WinMethodChannelBlue();

  static WinMethodChannelBlue get instance => _instance;
  final _messageConnector = BasicMessageChannel('flutter_blue_plus_win/message.connector', StandardMessageCodec());
  final MethodChannel _method = MethodChannel('flutter_blue_plus_win/method');

  final _eventScanResult = EventChannel('flutter_blue_plus_win/event.scanResult');
  final _eventPairedDevices = EventChannel('flutter_blue_plus_win/event.pairedDevices');

  WinMethodChannelBlue() {
    if (!Platform.isWindows) return;
    _messageConnector.setMessageHandler(_handleConnectorMessage);
    _eventScanResult.receiveBroadcastStream().listen((it) {
      FlutterBluePlusWin._scanResultsStreamController.add(BleDevice.fromJson(it.cast<String, dynamic>()));
    });
  }

  void _log(String message) {
    log(message);
  }

  Future<bool> isBluetoothAvailable() async {
    bool result = await _method.invokeMethod('isBluetoothAvailable');
    return result;
  }

  Future<bool> isSupported() async {
    bool result = await _method.invokeMethod('isSupported');
    return result;
  }

  Future<String> adapterName() async {
    return await _method.invokeMethod('adapterName');
  }

  Future<String> openBlue() async {
    return await _method.invokeMethod('openBlue');
  }

  Future<String> offBlue() async {
    return await _method.invokeMethod('offBlue');
  }

  Future<void> startScanPairedDevices() async {
    await _method.invokeMethod('startScanPairedDevices');
  }

  Future<void> stopScanPairedDevices() async {
    await _method.invokeMethod('stopScanPairedDevices');
  }

  void startScan({String? serviceUuid = ''}) {
    _method.invokeMethod('startScan', {'serviceUuid': serviceUuid ?? ''}).then((_) => _log('startScan success'));
  }

  void stopScan() {
    _method.invokeMethod('stopScan').then((_) => _log('stopScan success'));
  }

  Stream<dynamic> get _pairedDevicesStream => _eventPairedDevices.receiveBroadcastStream({'name': 'pairedDevices'});

  Stream<dynamic> get pairedDevicesStream => _pairedDevicesStream;

  Future<void> _handleConnectorMessage(dynamic message) async {
    final messageType = message['type'];
    if (messageType == 'log') {
      _log("原生日志打印 ${message['data']}");
      return;
    }
    _log('接收到原生消息 $message');
    if (messageType == 'OnConnectionStateChanged') {
      FlutterBluePlusWin._onConnectivityChanged(OnConnectionStateMessage.fromJson(message));
    } else if (messageType == 'OnDiscoveredServices') {
      FlutterBluePlusWin._onServiceDiscovered(OnDiscoverServicesMessage.fromJson(message));
    } else if (messageType == 'OnCharacteristicReceived') {
      FlutterBluePlusWin._onValueChanged(OnValueChangeData.fromJson(message, ValueChangeType.notify));
    } else if (messageType == 'OnDescriptorRead') {
      FlutterBluePlusWin._onValueChanged(OnValueChangeData.fromJson(message, ValueChangeType.read));
    } else if (messageType == 'OnMtuChanged') {
      final data = OnMtuConfigMessage.fromJson(message);
      if (data.success) {
        FlutterBluePlusWin._mtuCache[data.deviceId] = data.mtu;
        FlutterBluePlusWin._mtuConfigStreamController.add(data);
      }
    } else if (messageType == 'OnSetNotifyChanged') {
      FlutterBluePlusWin._setNotifyResultStreamController.add(OnSetNotifyResult.fromJson(message));
    } else if (messageType == 'OnAdapterStateChanged') {
      final data = OnAdapterStateChangedMessage.fromJson(message);
      FlutterBluePlusWin._adapterStateNow = data.state;
      FlutterBluePlusWin._adapterStateChangedStreamController.add(data);
    } else if (messageType == 'OnOpenBlueResult') {
      FlutterBluePlusWin._turnBlueResultStreamController.add(OnTurnBlueMessage.fromJson(message));
    }
  }

  Future<dynamic> connect(DeviceIdentifier deviceId) async {
    return _method.invokeMethod('connect', {
      'deviceId': deviceId.str,
    }).then((_) => _log('connect success'));
  }

  Future<dynamic> disconnect(DeviceIdentifier deviceId) async {
    return _method.invokeMethod('disconnect', {
      'deviceId': deviceId.str,
    }).then((_) => _log('disconnect success'));
  }

  Future<void> discoverServices(DeviceIdentifier deviceId) async {
    _log("discoverServices ${deviceId}");
    return await _method.invokeMethod('discoverServices', {
      'deviceId': deviceId.str,
    }).then((_) => _log('discoverServices success'));
  }

  Future<void> setNotifiable(DeviceIdentifier deviceId, Guid serviceUuid, Guid characteristicUuid, BleInputProperty bleInputProperty) async {
    _method.invokeMethod('setNotifiable', {
      'deviceId': deviceId.str,
      'service': serviceUuid.str128,
      'characteristic': characteristicUuid.str128,
      'bleInputProperty': bleInputProperty.value,
    }).then((_) => _log('setNotifiable success'));
  }

  Future<void> readValue(DeviceIdentifier deviceId, Guid serviceUuid, Guid characteristicUuid) async {
    _method.invokeMethod('readValue', {
      'deviceId': deviceId.str,
      'service': serviceUuid.str128,
      'characteristic': characteristicUuid.str128,
    }).then((_) => _log('readValue success, characteristic=$characteristicUuid'));
  }

  Future<void> writeValue(DeviceIdentifier deviceId, Guid serviceUuid, Guid characteristicUuid, Uint8List value, BleOutputProperty bleOutputProperty) async {
    _method.invokeMethod('writeValue', {
      'deviceId': deviceId.str,
      'service': serviceUuid.str128,
      'characteristic': characteristicUuid.str128,
      'value': value,
      'bleOutputProperty': bleOutputProperty.value,
    }).then((_) {
      _log('writeValue success, $value ,,characteristic=$characteristicUuid');
    }).catchError((onError) {
      // Characteristic sometimes unavailable on Android
      throw onError;
    });
  }

  Future<int> requestMtu(DeviceIdentifier deviceId, int expectedMtu) async {
    _log('[$deviceId], requestMtu, $expectedMtu');
    _method.invokeMethod('requestMtu', {
      'deviceId': deviceId.str,
      'expectedMtu': expectedMtu,
    }).then((_) => _log('requestMtu success'));
    return await FlutterBluePlusWin._mtuConfigStream.where((it) => it.deviceId.str == deviceId).map((it) => it.mtu).first;
  }
}
